﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Duellum.Core;
using JpLabs.Geometry;
using JpLabs.Extensions;
using Duellum.Map;
using System.Windows;

namespace Duellum.Game.Commands
{
	internal class MoveCommand : IDuelCommand
	{
		private readonly PositionCmdParam moveParam;
		private readonly DuelGame game;
		
		public MoveCommand(DuelGame game)
		{
			this.game = game;
			
			moveParam = new PositionCmdParam();
		}
		
		DuelTypeId IDuelCommand.Id
		{
			get { return DuelCoreCommands.Move; }
		}

		public bool CanExecute(IEnumerable<object> values)
		{
			PointInt targetPos;			
			return TryValidateValues(values, out targetPos);
		}
		
		private bool TryValidateValues(IEnumerable<object> values, out PointInt outTargetPos)
		{
			outTargetPos = default(PointInt);
			
			if (values == null) return false;
			
			object singleValue;
			if (!values.TrySingle(out singleValue)) return false; //invalid number of arguments
			
			if (!moveParam.TryCoerceValue(singleValue, out outTargetPos)) return false; //failed to coerce argument
			
			//TODO: Validation: Is inside map
			//TODO: Validation: Is not wall
			
			//Can move only a single postion per command
			//TODO: Use A-Star path finding
			{
				Point playerPoint = DuelMap.PositionToOriginPoint(game.CurrentPlayer.Position.Value);
				Point targetPoint = DuelMap.PositionToOriginPoint(outTargetPos);
				
				if ((targetPoint - playerPoint).LengthSquared > 1.01) return false;
			}
			return true;
		}

		public IDuelActionSequence Execute(IEnumerable<object> values)
		{
			PointInt targetPos;			
			if (!TryValidateValues(values, out targetPos)) throw new ArgumentException();
			
			//TODO: break command into an action sequence like: (TurnAction* + MoveAction)*
			
			var player = game.CurrentPlayer;
			var playerPos = player.Position.Value;
			
			//Turn
			int direction = GetTurnDirection(playerPos, targetPos);
			player.FaceDirection = direction;
			
			//Move
			if (targetPos.Dimensions == 2) targetPos = new PointInt(targetPos.X, targetPos.Y, playerPos.Z);
			
			player.Position = targetPos;
			
			return null;
		}
		
		static public int GetTurnDirection(PointInt from, PointInt to)
		{
			return GetTurnDirection(DuelMap.PositionToOriginPoint(from), DuelMap.PositionToOriginPoint(to));
		}
		
		static public int GetTurnDirection(Point from, Point to)
		{
			double angle = DuelMap.AngleBetwen(from, to);
			
			return DuelPlayer.AngleToDirection(angle);
		}
	}
}
